package com.bitdf.txing.chat.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.bitdf.txing.chat.constant.Constant;
import com.bitdf.txing.chat.entity.ChatMessageEntity;
import com.bitdf.txing.chat.entity.ReadMessageEntity;
import com.bitdf.txing.chat.feign.ChatGroupFeignService;
import com.bitdf.txing.chat.feign.ChatSearchFeignService;
import com.bitdf.txing.chat.feign.ChatUserFeignService;
import com.bitdf.txing.chat.service.ChatService;
import com.bitdf.txing.chat.service.ReadMessageService;
import com.bitdf.txing.chat.service.RedisService;
import com.bitdf.txing.chat.to.AddFriendRequestReplyTo;
import com.bitdf.txing.chat.to.GroupInfoTo;
import com.bitdf.txing.chat.to.GroupRequestAddTo;
import com.bitdf.txing.chat.vo.GroupOutlineVo;
import com.bitdf.txing.chat.vo.MessageSendVo;
import com.bitdf.txing.common.to.UserTo;
import com.bitdf.txing.common.utils.MyException;
import com.bitdf.txing.common.utils.R;
import com.bitdf.txing.common.vo.UserInfoVo;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.*;

/**
 * @author Lizhiwei
 * @date 2022/11/26 0:22:54
 * 注释：
 */
@Service
@Slf4j
public class ChatServiceImpl implements ChatService {
    @Autowired
    private RedisService redisService;
    @Autowired
    ChatSearchFeignService txSearchFeignService;
    @Autowired
    ChatGroupFeignService txGroupFeignService;
    @Autowired
    ChatUserFeignService txUserFeignService;
    @Autowired
    ReadMessageService readMessageService;

    @Override
    public void singleSend(ChatMessageEntity chatMessageEntity, ChannelHandlerContext ctx) {
//        ChatMessageEntity messageEntity = JSONObject.toJavaObject(param, ChatMessageEntity.class);
        chatMessageEntity.setCreateTime(new Date());
        //保存到redis

        boolean b = redisService.saveChatMessageToRedis(chatMessageEntity);

        ChannelHandlerContext context = Constant.onlineUser.get(chatMessageEntity.getToTargetId().toString());
        if (context == null) {
            //不在线，无需推送
        } else {
            String message = JSON.toJSONString(chatMessageEntity);
            sendMessage(context, message);
        }
    }

    @Override
    public void sendMessage(ChannelHandlerContext context, String message) {
        context.channel().writeAndFlush(new TextWebSocketFrame(message));
    }

    /**
     * 发送群聊消息
     * @param chatMessageEntity
     * @param ctx
     */
    @Override
    public void groupSend(ChatMessageEntity chatMessageEntity, ChannelHandlerContext ctx) {
        chatMessageEntity.setCreateTime(new Date());

        R r = txSearchFeignService.isGroupId(chatMessageEntity.getToTargetId());
        if (r.getCode() != 200) {
            String s = R.error("该群不存在").toString();
            sendMessage(ctx, s);
        }

        //保存到redis
        boolean b = redisService.saveChatMessageToRedis(chatMessageEntity);

        R r1 = txGroupFeignService.getMemberIdsByGroupId(chatMessageEntity.getToTargetId());
        List<Long> list = r1.getData(new TypeReference<List<Long>>() {
        });

        String s = JSON.toJSONString(chatMessageEntity);

        ChannelHandlerContext context = null;
        for (Long id : list) {
            context = Constant.onlineUser.getOrDefault(id.toString(), null);
            if (context != null) {
                sendMessage(context, s);
            }
        }
    }


    /**
     * 获取在线好友
     *
     * @param friendsList
     * @return
     */
    @Override
    public List<Long> getOnlineFriends(List<UserInfoVo> friendsList) {
        if (friendsList == null || friendsList.size() == 0) {
            return null;
        }
        ArrayList<Long> onlineFriends = new ArrayList<>();
        for (UserInfoVo userTo : friendsList) {
            if (Constant.onlineUser.containsKey(String.valueOf(userTo.getId()))) {
                onlineFriends.add(userTo.getId());
            }
        }
        return onlineFriends;
    }

    /**
     * 发起加入团队申请 并且实时通知双方
     * @param groupRequestAddTo
     * @param ctx
     * @throws MyException
     * @throws IOException
     */
    @Override
    public void groupRequestSend(GroupRequestAddTo groupRequestAddTo, ChannelHandlerContext ctx) throws MyException, IOException {
        //1、远程保存组团请求
        groupRequestAddTo.setCreateTime(new Date());
        groupRequestAddTo.setStatus(0);
        try {
            R r = txGroupFeignService.save(groupRequestAddTo);
            groupRequestAddTo = r.getData(new TypeReference<GroupRequestAddTo>() {
            });
        } catch (Exception e) {
            throw new MyException("远程操作时出现了异常");
        }
        //2、发送通知
        Set<Long> hashSet = new HashSet<>();
        hashSet.add(groupRequestAddTo.getGroupId());
        R r1 = txSearchFeignService.getGroupSimpleByGroupIds(hashSet);
        List<GroupOutlineVo> groupOutlineList = r1.getData(new TypeReference<List<GroupOutlineVo>>() {
        });
        GroupOutlineVo outlineVo = null;
        if (groupOutlineList != null && groupOutlineList.size() != 0) {
            outlineVo = groupOutlineList.get(0);
            groupRequestAddTo.setLeaderId(outlineVo.getUserId());
        }

        R r2 = txUserFeignService.getUserById(groupRequestAddTo.getRequestUserId());
        UserTo userTo = r2.getData(new TypeReference<UserTo>() {
        });

        Map<String, Object> hashMap = new HashMap<>();
        hashMap.put("groupOutLine", outlineVo);
        hashMap.put("requestUserInfo", userTo);
        hashMap.put("groupRequestAddInfo", groupRequestAddTo);
        R ok = R.ok().put("data", hashMap);

        String s = JSON.toJSONString(ok);

        ChannelHandlerContext context = null;
        context = Constant.onlineUser.getOrDefault(outlineVo.getUserId().toString(), null);
        if (context != null) {
            sendMessage(context, s);
        }
        sendMessage(ctx, s);
    }

    /**
     * 同意或拒绝别人的加入团队申请
     * @param groupRequestAddTo1
     * @param ctx
     * @throws IOException
     */
    @Override
//    @Transactional
    public void agreeOrRejectGroupRequest(GroupRequestAddTo groupRequestAddTo1, ChannelHandlerContext ctx) throws IOException {
        if (groupRequestAddTo1.getStatus() == 1) {
            //1、远程同意
            //1）修改状态
            R r = txGroupFeignService.agreeGroupRequest(groupRequestAddTo1.getId());

            // 2) 把新成员添加到数据库的相关表中
            R r1 = txGroupFeignService.addGroupMember(groupRequestAddTo1.getGroupId(), groupRequestAddTo1.getRequestUserId());
            //3) 把es中目前成员数+1
            R r3 = txSearchFeignService.groupInfoIncreOrDcre(groupRequestAddTo1.getGroupId(), 1, "currentPersonCount");


            //2、发送通知
            Set<Long> set = new HashSet<>();
            set.add(groupRequestAddTo1.getGroupId());
            R r2 = txSearchFeignService.getGroupSimpleByGroupIds(set);
            List<GroupOutlineVo> outlineVoList = r2.getData(new TypeReference<List<GroupOutlineVo>>() {
            });
            GroupOutlineVo groupOutlineVo = null;
            if (outlineVoList != null && outlineVoList.size() > 0) {
                groupOutlineVo = outlineVoList.get(0);
            }
            Map<String, Object> hashMap = new HashMap<>();
            hashMap.put("groupOutlineInfo", groupOutlineVo);
            //获取目前组团人数
            R r4 = txSearchFeignService.getGroupInfoById(groupRequestAddTo1.getGroupId());
            GroupInfoTo groupInfoTo = r4.getData(new TypeReference<GroupInfoTo>() {
            });
            hashMap.put("currentPersonCount", groupInfoTo.getCurrentPersonCount());
            hashMap.put("context", "欢迎新成员加入了团队，大家可以开始聊天啦");
            hashMap.put("type", Constant.AGREE_OR_REJECT_GROUP_REQUEST);
            R ok = R.ok().put("data", hashMap);
            String s = JSON.toJSONString(ok);

            //1) 通知组团新旧成员
            //获取组团成员ids
            R r5 = txGroupFeignService.getGroupAndUserIdsByUserId(groupOutlineVo.getUserId());
            Map<String, List<Long>> groupUserIdsMap = r5.getData(new TypeReference<Map<String, List<Long>>>(){});
//            Map<String, List<Long>> groupUserIdsMap = (Map<String, List<Long>>) r5.get("data");
            List<Long> memberIds = groupUserIdsMap.get(groupOutlineVo.getId().toString());
            ChannelHandlerContext context = null;
            for (Long memberId : memberIds) {
                context = Constant.onlineUser.getOrDefault(memberId.toString(), null);
                if (context != null) {
                    sendMessage(context, s);
                }
            }
        } else if (groupRequestAddTo1.getStatus() == 2) {
            //拒绝
            //1）修改状态
            R r = txGroupFeignService.rejectGroupRequest(groupRequestAddTo1.getId());

            Map<String, Object> hashMap = new HashMap<>();
            //获取目前组团人数
            hashMap.put("type", Constant.AGREE_OR_REJECT_GROUP_REQUEST);
            hashMap.put("groupRequestInfo", groupRequestAddTo1);
            R ok = R.ok().put("data", hashMap);
            String s = JSON.toJSONString(ok);

            //通知自己
            sendMessage(ctx, s);
            ChannelHandlerContext context = Constant.onlineUser.getOrDefault(groupRequestAddTo1.getRequestUserId().toString(), null);
            if (context != null) {
                // 通知对方
                sendMessage(context, s);
            }

        }

    }


    /**
     * 申请加为好友时 给对方实时发通知
     * @param addFriendRequestReplyTo
     */
    @Override
    public void addFriendRequestReply(AddFriendRequestReplyTo addFriendRequestReplyTo) {
        Long receiveUserId = addFriendRequestReplyTo.getAddFriendRequestInfo().getReceiveUserId();
        ChannelHandlerContext context = Constant.onlineUser.getOrDefault(receiveUserId.toString(), null);
        if (context != null) {
            String s = JSON.toJSONString(R.ok().put("data", new MessageSendVo<AddFriendRequestReplyTo>(addFriendRequestReplyTo, "add_friend_request_reply")));
            sendMessage(context, s);
        }
    }

    /**
     * 同意或拒绝对方的好友申请后 实时通知对方
     * @param addFriendRequestReplyTo1
     */
    @Override
    public void agreeOrRejectFriendRequest(AddFriendRequestReplyTo addFriendRequestReplyTo1) {
        Long sendUserId = addFriendRequestReplyTo1.getAddFriendRequestInfo().getSendUserId();
        ChannelHandlerContext context = Constant.onlineUser.getOrDefault(sendUserId.toString(), null);
        if (context != null) {
            String s = JSON.toJSONString(R.ok().put("data", new MessageSendVo<AddFriendRequestReplyTo>(addFriendRequestReplyTo1, "agree_or_reject_friend_request")));
            sendMessage(context, s);
        }
    }

    /**
     * 用户连接websocket服务器后会通过该方法注册，也就是保存在线用户信息
     * @param param
     * @param ctx
     */
    @Override
    public void register(JSONObject param, ChannelHandlerContext ctx) {
        String id = (String) param.get("id");
        Constant.onlineUser.put(id, ctx);
    }

    @Override
    public void readReplySend(ReadMessageEntity entity, ChannelHandlerContext ctx) {
        entity.setReadTime(new Date());
        try {
            //TODO 当数据库同步时要注意了，因为有可能数据库中的数据比redis中的要新
            boolean b = redisService.updateReadMessageTime(entity);
        } catch (Exception e) {
            try {
                ReadMessageEntity readMessageEntity = readMessageService.getReadMessage(entity.getReadUserId(), entity.getSendUserId());
                if (readMessageEntity == null) {
                    boolean save = readMessageService.save(entity);
                } else {
                    boolean update = readMessageService.update(new UpdateWrapper<ReadMessageEntity>().eq("read_user_id", entity.getReadUserId())
                            .eq("send_user_id", entity.getSendUserId()).set("read_time", entity.getReadTime()));
                }
                log.error("【Redis】 redis更新已读回执时出错 。已保存到数据库。");
            } catch (Exception ex) {
                log.error("【Redis】 redis更新已读回执时出错，发送回执id。保存到数据库过程中失败。回执丢失！");
//                throw new RuntimeException(ex);
            }
        }

        ChannelHandlerContext context = Constant.onlineUser.getOrDefault(entity.getSendUserId().toString(), null);
        if (context != null) {
            R ok = R.ok().put("data", new MessageSendVo<ReadMessageEntity>(entity, "read_reply_sending"));
            sendMessage(context, JSON.toJSONString(ok));
        }
    }

    @Override
    public void typeError(ChannelHandlerContext ctx) {
        R error = R.error().put("data", new MessageSendVo<String>("信息类型错误", Constant.TYPE_ERROR));
        sendMessage(ctx, JSON.toJSONString(error));
    }

    @Override
    public void remove(ChannelHandlerContext ctx) {
        Iterator<Map.Entry<String, ChannelHandlerContext>> iterator =
                Constant.onlineUser.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, ChannelHandlerContext> entry = iterator.next();
            if (entry.getValue() == ctx) {
                Constant.webSocketServerHandshakerMap.remove(ctx.channel().id().asLongText());
                iterator.remove();
                break;
            }
        }
    }
}
